#ifdef __LINUX_PAL__
#include <string.h>
#include <curl/curl.h>
#include <sys/stat.h>
#elif defined (__ESP_32_PAL__)
#include "esp_tls.h"
#endif

#include "ola_socket.h"
#include "ola_http.h"
#include "ola_string.h"
#include "ola_memory.h"
#include "ola_stdio.h"
#include "ola_log.h"


#ifdef __LINUX_PAL__
typedef struct _data_st{
	char *buf;
	int len;
}https_data_st;

static size_t CLstaticWriteCallbackLocked(char* ptr, size_t size, size_t nmemb, void* userdata) {
	size_t all_size = 0;
	https_data_st *data = NULL;

	if (!userdata) {
		return 0;
	}

	data = (https_data_st*)userdata;
	ola_pal_memset(data, 0, sizeof(https_data_st));

	all_size = size * nmemb;

	if(data->buf != NULL){
		ola_pal_free(data->buf);
		data->buf = NULL;
	}

	data->buf = (char *)ola_pal_malloc(all_size);

	ola_pal_memcpy(data->buf, ptr, all_size);
	data->len = all_size;

	return all_size;
}

/*
 * @brief 异步https协议post数据
 * @param[in]  url: post remote url addr
 * @param[in]  slist: post header paramer list
 * @param[int] body: post data
 * @param[out] data: response data
 *
 * */
int32_t ola_pal_https_post(char *url, char *slist, char *body, void *data)
{
	CURL* ola_pal_curl = NULL;
	CURLcode ret;
	struct curl_slist* list = NULL;

	int flag = 0;

	if (url == NULL || body == NULL || data == NULL){
		log_error("ola_pal_https_post paramer is null\n");
		goto RET; 
	}

	ola_pal_curl = curl_easy_init();
	if (ola_pal_curl == NULL) {
		log_error("ola_pal_https_put curl init error!\n");
		flag = -1;
		goto RET;
	}

	if (curl_easy_setopt(ola_pal_curl, CURLOPT_SSL_VERIFYPEER, false)) {
		log_error("ola_pal_https_init set ssl error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_WRITEFUNCTION, CLstaticWriteCallbackLocked)) {
		log_error("ola_pal_https_init set writefunction error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_VERBOSE, 0)) {
		log_error("ola_pal_https_init set verbose error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_NOSIGNAL, true)) {
		log_error("ola_pal_https_init set nosinal error!\n");
		flag = -1;
		goto RET;
	}

#if 0
	ret = curl_easy_setopt(ola_pal_curl, CURLOPT_CAINFO, "/data/misc/javs/ca-certificates.crt");
#else
	ret = curl_easy_setopt(ola_pal_curl, CURLOPT_CAINFO, "/etc/ssl/certs/ca-certificates.crt");
#endif
	if (ret != CURLE_OK) {
		log_error("ola_pal_https_init set ca error!\n");
		flag = -1;
	}

	if (curl_easy_setopt(ola_pal_curl, CURLOPT_CONNECTTIMEOUT, 100)) { 
		log_error("ola_pal_https_post set connect timeout error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_TIMEOUT, 10)) {
		log_error("ola_pal_https_post set timeout error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_URL, url)) {
		log_error("ola_pal_https_post set url error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_POSTFIELDS, body)) {
		log_error("ola_pal_https_post set body error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_WRITEDATA, data)) {
		log_error("ola_pal_https_post set write data error!\n");
		flag = -1;
		goto RET;
	}
	if (curl_easy_setopt(ola_pal_curl, CURLOPT_POSTFIELDSIZE, strlen(body))) {
		log_error("ola_pal_https_post set body len error!\n");
		flag = -1;
		goto RET;
	}

	if(slist != NULL){
		list = curl_slist_append(list, slist);
	}
	list = curl_slist_append(list, "content-type: application/x-www-form-urlencoded; charset=utf-8");

	if (curl_easy_setopt(ola_pal_curl, CURLOPT_HTTPHEADER, list)) {
		log_error("curl_easy_setopt set list error!\n");
		flag = -1;
		goto RET;
	}

	ret = curl_easy_perform(ola_pal_curl);
	if (ret != CURLE_OK)
	{
		log_error("curl_easy_perform error: %d\n", ret);
		flag = -1;
		goto RET;
	}
RET:
	if (list != NULL){
		ola_pal_free(list);
	}

	if (ola_pal_curl != NULL){
		curl_easy_cleanup(ola_pal_curl);
		ola_pal_curl = NULL;
	}
	return flag;
}
#endif // __LINUX_PAL__

static int ola_dev_http_parse_content(
	char *response, 
	int response_len, 
	char *content,
	int content_len)
{
	int length = 0;
	content[0] = 0;
    char* p = ola_pal_strstr(response, "\r\n\r\n");
    if (p == NULL) 
	{
		return -1;
	}
	p += 4;
	length = response_len - (p - response);
	length = length > content_len ? content_len - 1 : length;
	ola_pal_strncpy(content, p, length);
	content[length] = 0;
	// log_info("content = \r\n%s", content);
    
	return length;
}

/**
 * @name:实现HTTPS的POST请求,请求响应填入revbuf参数 
 *
 * @param[in]: host POST请求的目标地址
 * @param[in]: query POST请求的路径、HEADER和Payload
 * @param[out]: revbuf 填入请求的响应信息
 * @param[in]: buflen  revbuf最大长度
 *
 * @returns:   负数 - 发生错误, 非负数 - 实际填充revbuf的长度 
 *
 */

int ola_dev_https_post( char* host, char* query ,char *revbuf,int buflen)
{

#ifdef __LINUX_PAL__
	https_data_st data;

	//printf("body: %s\n", query);

	memset(&data, 0, sizeof(https_data_st));

	//int ret = ola_pal_https_post(host, "Connection: close", strstr(query, "\r\n\r\n")+4, &data);
	int ret = ola_pal_https_post(host, NULL, query, &data);
	if(ret < 0){
		log_error("post error\n");
	}

	if(data.len <= 0){
		log_error("https recv error\n");
		return -1;
	}

	if(buflen >= data.len){
		ola_pal_memcpy(revbuf, data.buf, data.len);
	}else{
		log_error("buffer too smaill\n\n");
	}

	if(data.buf != NULL){
		ola_pal_free(data.buf);
		data.buf = NULL;
	}

	return data.len;
#elif defined (__ESP_32_PAL__)
    int len = 0;
	
    esp_tls_cfg_t config = {
        // .skip_common_name = true,
    };
    esp_tls_t *tls = NULL;
    printf("ola_dev_https_post host:%s\r\n", host);
    memset(revbuf, 0x0, buflen);
    tls = esp_tls_conn_new(host, strlen(host), 443, &config);
	if (tls == NULL)
	{
		ola_pal_printf("esp_tls_conn_new failed\n");
		return -1;
	}
	
    esp_tls_conn_write(tls, query, strlen(query));

    char temp_buf[2024];
    uint32_t temp_size = sizeof(temp_buf);
    
    len = esp_tls_conn_read(tls, temp_buf, temp_size);
    esp_tls_conn_delete(tls);

    if (len >= temp_size) {
        len = temp_size - 1;
    }
    temp_buf[len] = 0;
	log_info("... read data length = %d, response data = \r\n%s", len, temp_buf);
	len = ola_dev_http_parse_content(temp_buf, len, revbuf, buflen);
    // printf("revbuf:%d, %s\r\n",len,revbuf);

    return len;
#else
    return -1;
#endif
}

/**
 * @brief 实现HTTP的POST请求,请求响应填入revbuf参数.
 * 
 * @param[in]  host: POST请求的目标主机
 * @param[in]  query: POST请求的路径、HEADER和Payload
 * @param[out] revbuf: 填入请求的响应信息的Body
 * @param[in]  buflen: revbuf的最大长度
 * 
 * @returns: 负值 - 发生错误, 非负 - 实际填充revbuf的长度
 * 
 * @note: 此函数必须正确实现,否者设备无法校时,无法正常激活绑定
 *
 * */
int ola_dev_http_post( char* host, char* query, char *revbuf, int buflen)
{
#if defined (__LINUX_PAL__) || defined (__ESP_32_PAL__)
	int log_socket = -1;
	int len = 0;
    int ret = -1;
    char *recv_buf = NULL;
    ola_pal_sockaddr_in saServer; 
	char ip[20] = {0};

    if(host == NULL || query == NULL || revbuf == NULL) {
        log_error("DNS lookup failed");
        goto RET;
    }

    ola_pal_memset(ip,0,sizeof(ip)); 
	ret = ola_pal_gethostbyname(host, ip, SOCKET_IP_ADDR_LEN);
	if(ret < 0){
		log_error("get ip error");
		ret = -1;
		goto RET;
	}
	
	ola_pal_memset(&saServer, 0, sizeof(saServer));
    saServer.sin_family = ola_pal_get_socket_proto_domain(OLA_PAL_SOCK_PROTO_DOMAIN_AF_INET);
    saServer.sin_port = ola_pal_htons(80);
	saServer.sin_addr.s_addr = ola_pal_inet_addr(ip);

    log_socket = ola_pal_socket(OLA_PAL_SOCK_PROTO_DOMAIN_AF_INET, OLA_PAL_SOCK_PROTO_TYPE_SOCK_STREAM, OLA_PAL_SOCK_PROTO_PROTO_IPPROTO_TCP);
    if(log_socket < 0) {
        log_error("... Failed to allocate socket.");
        goto RET;
    }
	int reuseaddrEnable = 1;
	if (ola_pal_setsockopt(log_socket, 
                OLA_PAL_SOCK_OPT_LEVEL_SOL_SOCKET,
                OLA_PAL_SOCK_OPT_NAME_SO_REUSEADDR, 
                (uint8_t *)&reuseaddrEnable, 
                sizeof(reuseaddrEnable)) < 0){
		log_error("set SO_REUSEADDR error");
	}
	
    /*fcntl(log_socket,F_SETFL,fcntl(log_socket,F_GETFL,0)|O_NONBLOCK);*/

    if(ola_pal_connect(log_socket, (ola_pal_sockaddr *)&saServer, sizeof(saServer)) != 0)
	{
        log_error("... socket connect failed");
        goto RET;
    }

    if (ola_pal_send(log_socket, query, ola_pal_strlen(query), 0, 5000) < 0) {
        log_error("... socket send failed");
        goto RET;
    }

	int recv_buf_len = 1024; //2048;
	recv_buf = (char *)ola_pal_malloc(recv_buf_len);
	if(recv_buf == NULL)
	{
		goto RET;
	}
	ola_pal_memset(recv_buf, 0, recv_buf_len);
	len = ola_pal_recv(log_socket, recv_buf, recv_buf_len, 0, 5000);
	if(len <= 0)
	{
		ret = -1;
		goto RET;
	}
	log_debug("... read data length = %d, response data = \r\n%s", len, recv_buf);
	ret = ola_dev_http_parse_content(recv_buf, len, revbuf, buflen);


RET:
	if(-1 != log_socket){
		ola_pal_close(log_socket);
	}
	if (recv_buf)
	{
		/* code */
		ola_pal_free(recv_buf);
	}
	
	return ret;
#else
    return -1;
#endif

}

